package com.lzh.runke.core.web_base.exception.handler;

import com.lzh.runke.core.web_base.enums.ProfileEnum;
import com.lzh.runke.core.web_base.exception.BusinessException;
import com.lzh.runke.core.web_base.exception.ForbiddenException;
import com.lzh.runke.core.web_base.exception.UnauthorizedException;
import com.lzh.runke.core.web_base.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.annotation.Resource;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Description: 全局异常捕获
 * @ExceptionHandler 注解我们一般是用来自定义异常的。
 * 可以认为它是一个异常拦截器（处理器）。
 * @Author: LiuZiHao
 * @Date: 2022/5/25 09:39
 */
@Order(0)
@Primary
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    private static final String GLOBAL_MESSAGE = "服务器异常，请联系管理员";

    @Resource
    private Environment environment;

    /**
     * 运行时异常
     *
     * @param ex 异常信息对象
     * @return Result<Void>
     */
    @ExceptionHandler(RuntimeException.class) //申明捕获那个异常类
    public Result<Void> runtimeExceptionHandler(RuntimeException ex) {
        return resultFormatter("运行时异常：" + ex.getMessage(), ex);
    }

    /**
     * 空指针异常
     *
     * @param ex 异常信息对象
     * @return Result<Void>
     */
    @ExceptionHandler(NullPointerException.class)
    public Result<Void> nullPointerExceptionHandler(NullPointerException ex) {
        return resultFormatter("空指针异常", ex);
    }

//    HttpMessageNotReadableException

    /**
     * 类型转换异常
     *
     * @param ex 异常信息对象
     * @return Result<Void>
     */
    @ExceptionHandler(ClassCastException.class)
    public Result<Void> classCastExceptionHandler(ClassCastException ex) {
        return resultFormatter("类型转换异常", ex);
    }

    /**
     * IO异常
     *
     * @param ex 异常信息对象
     * @return Result<Void>
     */
    @ExceptionHandler(IOException.class)
    public Result<Void> iOExceptionHandler(IOException ex) {
        return resultFormatter("IO异常", ex);
    }

    /**
     * 方法未找到异常
     *
     * @param ex 异常信息对象
     * @return Result<Void>
     */
    @ExceptionHandler(NoSuchMethodException.class)
    public Result<Void> noSuchMethodExceptionHandler(NoSuchMethodException ex) {
        return resultFormatter("方法未找到异常", ex);
    }

    /**
     * 数组越界异常
     *
     * @param ex 异常信息对象
     * @return Result<Void>
     */
    @ExceptionHandler(IndexOutOfBoundsException.class)
    public Result<Void> indexOutOfBoundsExceptionHandler(IndexOutOfBoundsException ex) {
        return resultFormatter("数组越界异常", ex);
    }

    /**
     * 栈溢出
     *
     * @param ex 异常信息对象
     * @return Result<Void>
     */
    @ExceptionHandler(StackOverflowError.class)
    public Result<Void> stackOverflowErrorHandler(StackOverflowError ex) {
        return resultFormatter("栈溢出", ex);
    }

    /**
     * 除数不能为0
     *
     * @param ex 异常信息对象
     * @return Result<Void>
     */
    @ExceptionHandler(ArithmeticException.class)
    public Result<Void> arithmeticExceptionHandler(ArithmeticException ex) {
        return resultFormatter("除数不能为0", ex);
    }

    /**
     * 其他异常
     *
     * @param ex 异常信息对象
     * @return Result<Void>
     */
    @ExceptionHandler(Exception.class)
    public Result<Void> exception(Exception ex) {
        return resultFormatter("其他异常", ex);
    }

    /**
     * 参数错误
     *
     * @param ex 异常信息对象
     * @return Result<Void>
     */
    @ExceptionHandler(IllegalArgumentException.class)
    public Result<Void> illegalArgumentExceptionHandle(IllegalArgumentException ex) {
        return resultFormatter(ex.getMessage(), ex);
    }

    /**
     * 数据库查询异常
     *
     * @param ex 异常信息对象
     * @return Result<Void>
     */
    @ExceptionHandler(SQLException.class)
    public Result<Void> sqlLServerExceptionHandler(SQLException ex) {
        return resultFormatter("数据库查询异常", ex);
    }

    /**
     * 请求参数缺失异常
     *
     * @param ex 异常信息对象
     * @return Result<Void>
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public Result<Void> missingParamExceptionHandler(MissingServletRequestParameterException ex) {
        return resultFormatter("请求参数缺失异常", ex);
    }

    /**
     * 业务异常
     *
     * @param ex 异常信息对象
     * @return Result<Void>
     */
    @ExceptionHandler(BusinessException.class)
    public Result<Void> baseServiceExceptionHandler(BusinessException ex) {
        return resultFormatter(ex.getMessage(), ex);
    }

    /**
     * 校验异常，request封装方式
     *
     * @param ex 异常信息对象
     * @return Result<Void>
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<Void> notValidException(MethodArgumentNotValidException ex) {
        List<String> errorInfo = ex.getBindingResult().getAllErrors()
                .stream()
                .map(ObjectError::getDefaultMessage)
                .collect(Collectors.toList());
        return resultFormatter(errorInfo.toString(), ex);
    }

    /**
     * 校验异常，注解封装方式
     *
     * @param ex 异常信息对象
     * @return Result<Void>
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public Result<Void> notValidException(ConstraintViolationException ex) {
        List<String> errorInfo = ex.getConstraintViolations()
                .stream()
                .map(ConstraintViolation::getMessage)
                .collect(Collectors.toList());
        return resultFormatter(errorInfo.toString(), ex);
    }

    /**
     * 禁止访问异常
     *
     * @param ex 异常信息对象
     * @return Result<Void>
     */
    @ResponseStatus(code = HttpStatus.FORBIDDEN, reason = "禁止访问")
    @ExceptionHandler(ForbiddenException.class)
    public Result<Void> forbiddenException(ForbiddenException ex) {
        return resultFormatter(ex.getMessage(), ex);
    }

    /**
     * 禁止访问异常
     *
     * @param ex 异常信息对象
     * @return Result<Void>
     */
    @ResponseStatus(code = HttpStatus.UNAUTHORIZED, reason = "未授予权限")
    @ExceptionHandler(UnauthorizedException.class)
    public Result<Void> unauthorizedException(UnauthorizedException ex) {
        return resultFormatter(ex.getMessage(), ex);
    }

    /**
     * 返回值格式化
     *
     * @param message 异常描述信息
     * @param ex      异常信息对象
     * @return Result<Void>
     */
    private <T extends Throwable> Result<Void> resultFormatter(String message, T ex) {
        log.error(message + ": {}", ExceptionUtils.getStackTrace(ex));
        if (ArrayUtils.contains(environment.getActiveProfiles(), ProfileEnum.PRODUCTION.getValue()) && !(ex instanceof BusinessException)) {
            message = GLOBAL_MESSAGE;
        }
        return Result.<Void>builder().success(false).message(message).build();
    }
}
